#ifndef SQL_DB_INPUT_BOX_H_JM_20120720
#define SQL_DB_INPUT_BOX_H_JM_20120720

// ##
#include "InputBox.h"
#include "../../common/SQL/BoxCellDataImporterVisitorImpl.h"
#include "../../common/SQL/ConcreteCellValueSetterVisitor.h"
// ## C++
#include <vector>
#include <string>
// ## ODBC
#include <sqltypes.h>

namespace jm4bbx {


	class SqlDBInputBoxImpl {
	public:
		static SQLCHAR* CONNECTION_STRING;
		static const int STRING_DATA_SIZE = 64;
		static const int INTEGER_DATA_SIZE = 0;
		typedef char StringDataType[STRING_DATA_SIZE];
		typedef int IntegerDataType;

		/// Typ stlpec dat.
		typedef std::vector<BaseCellPtr> ColumnType;
		/// Typ celej tabulky dat.
		typedef std::vector<ColumnType> TableType;
		/// Typ popisu typov v tabulke.
		typedef std::vector<int> ColumnSQLTypes;

		/// Vykonaj (posli, prijmi a spracuj) SQL statement
		void executeStatement(const std::string& statement);

		size_t getColumnSize() const;	

		template<class T>
		void loadData(T* realBoxThis) {
			const size_t columns = data_.size();
			const size_t rows = getColumnSize();
			BoxCellDataImporterVisitorImpl<T> visitor(realBoxThis);
			for (size_t columnIndex = 0; columnIndex < columns; ++columnIndex) {
				for (size_t rowIndex = 0; rowIndex < rows; ++ rowIndex) {

					BaseCellPtr val = data_[columnIndex][rowIndex];

					visitor.setContext(rowIndex, columnIndex);
					val->acceptVisitor(&visitor);

				}
			}
		}

	private:
		SqlDBInputBoxImpl::TableType data_;
		SqlDBInputBoxImpl::ColumnSQLTypes types_;

		SQLHANDLE enviromentHandle_;    
		SQLHANDLE connectionHandle_;
		SQLHANDLE statementHandle_;

		/// Uvolnenie alokovanych SQL handlerov potrebnych pre komunikaciu s DB.
		void releaseSQLHandles();
		/// Inicializacia prostredia pre ODBC komunikaciu (krok 1).
		bool initEnviroment();
		/// Spojenie s ODBC (krok2).
		bool connect(); 
		/// Zaslanie statementu skrz ODBC do DB (krok 3).
		bool sendStatement(const std::string& statement);
	};

	//template<class InputDescriptor>
	class SqlDBInputBox : public InputBox/*<InputDescriptor>*/ {

	public:
		/**
		 * Pocas konstrukcie boxu sa vykonava spojenie s DB a stahovanie dat.
		 * Stahovanie sa neda paralelizovat a teda je jedno ci sa data 
		 * predpripravia skor. Naviac to nasledne nezdrzuje vykonavanie vypoctu
		 * modelu kde je ziskanie dat len podporna cinnost.
		 **/
		SqlDBInputBox(bobox::box_id_type id, 
			bobox::box_tid_type tid, bobox::request *req, 
			const bobox::parameters &params, const std::string& statement) 
			: InputBox(id, tid, req, params) {
				pimpl_.executeStatement(statement);
		}

		virtual size_t getColumnSize() const override  {
			return pimpl_.getColumnSize();
		}		
		/**
		 * Nastavenie hodnoty do tabulky dat.
		 * @param columnIndex index stlpca do ktoreho sa ma vlozit hodnota
		 * @param rowIndex index riadku do ktoreho sa ma vlozit hodnota
		 * @param value vkaldana hodnota
		 * 
		 * @tparam T typ vkladanej hodnoty - musi zodpovedat typu cielovej bunky
		 **/
		template<class policy, class T>
		void setData(const size_t columnIndex, const size_t rowIndex, T value) {
			using namespace bobox;
			get_data<policy>(columnType(columnIndex))[rowIndex] = value;
		}

	protected:

		virtual void loadData() override {
			pimpl_.loadData(this);
		}



	private:
		SqlDBInputBoxImpl pimpl_;
	};

	//template<class InputDescriptor>
	//SQLCHAR* jm4bbx::SqlDBInputBox<InputDescriptor>::CONNECTION_STRING = 
	//	(SQLCHAR*)"DSN=Diplomka;";


} // namespace jm4bbx

#endif